// Package admin
// @Link  https://github.com/bufanyun/hotgo
// @Copyright  Copyright (c) 2023 HotGo CLI
// @Author  Ms <133814250@qq.com>
// @License  https://github.com/bufanyun/hotgo/blob/master/LICENSE
package admin

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/crypto/gmd5"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/util/grand"
	"hotgo/internal/consts"
	"hotgo/internal/dao"
	"hotgo/internal/library/cache"
	"hotgo/internal/library/contexts"
	"hotgo/internal/library/token"
	"hotgo/internal/model"
	"hotgo/internal/model/entity"
	"hotgo/internal/model/input/adminin"
	"hotgo/internal/model/input/apiin"
	"hotgo/internal/model/input/sysin"
	"hotgo/internal/service"
	"hotgo/utility/simple"
	"hotgo/utility/tree"
	"time"
)

type sAdminSite struct{}

func NewAdminSite() *sAdminSite {
	return &sAdminSite{}
}

func init() {
	service.RegisterAdminSite(NewAdminSite())
}

// Register 账号注册
func (s *sAdminSite) Register(ctx context.Context, in *adminin.RegisterInp) (err error) {
	config, err := service.SysConfig().GetLogin(ctx)
	if err != nil {
		return
	}

	if config.ForceInvite == 1 && in.InviteCode == "" {
		err = gerror.New("请填写邀请码")
		return
	}

	var data adminin.MemberAddInp

	// 默认上级
	data.Pid = 1

	// 存在邀请人
	if in.InviteCode != "" {
		pmb, err := service.AdminMember().GetIdByCode(ctx, &adminin.GetIdByCodeInp{Code: in.InviteCode})
		if err != nil {
			return err
		}

		if pmb == nil {
			err = gerror.New("邀请人信息不存在")
			return err
		}

		data.Pid = pmb.Id
	}

	if config.RegisterSwitch != 1 {
		err = gerror.New("管理员未开放注册")
		return
	}

	if config.RoleId < 1 {
		err = gerror.New("管理员未配置默认角色")
		return
	}

	if config.DeptId < 1 {
		err = gerror.New("管理员未配置默认部门")
		return
	}

	// 验证唯一性
	err = service.AdminMember().VerifyUnique(ctx, &adminin.VerifyUniqueInp{
		Where: g.Map{
			dao.AdminMember.Columns().Username: in.Username,
			dao.AdminMember.Columns().Mobile:   in.Mobile,
		},
	})
	if err != nil {
		return
	}

	// 验证短信验证码
	err = service.SysSmsLog().VerifyCode(ctx, &sysin.VerifyCodeInp{
		Event:  consts.SmsTemplateRegister,
		Mobile: in.Mobile,
		Code:   in.Code,
	})
	if err != nil {
		return
	}

	data.MemberEditInp = &adminin.MemberEditInp{
		Id:       0,
		RoleId:   config.RoleId,
		PostIds:  config.PostIds,
		DeptId:   config.DeptId,
		Username: in.Username,
		Password: in.Password,
		RealName: "",
		Avatar:   config.Avatar,
		Sex:      3, // 保密
		Mobile:   in.Mobile,
		Status:   consts.StatusEnabled,
	}
	data.Salt = grand.S(6)
	data.InviteCode = grand.S(12)
	data.PasswordHash = gmd5.MustEncryptString(data.Password + data.Salt)
	data.Level, data.Tree, err = service.AdminMember().GenTree(ctx, data.Pid)
	if err != nil {
		return
	}

	// 提交注册信息
	return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
		id, err := dao.AdminMember.Ctx(ctx).Data(data).OmitEmptyData().InsertAndGetId()
		if err != nil {
			err = gerror.Wrap(err, consts.ErrorORM)
			return
		}

		// 更新岗位
		if err = service.AdminMemberPost().UpdatePostIds(ctx, id, config.PostIds); err != nil {
			err = gerror.Wrap(err, consts.ErrorORM)
		}
		return
	})
}

// AccountLogin 账号登录
func (s *sAdminSite) AccountLogin(ctx context.Context, in *adminin.AccountLoginInp) (res *adminin.LoginModel, err error) {
	defer func() {
		service.SysLoginLog().Push(ctx, &sysin.LoginLogPushInp{Response: res, Err: err})
	}()

	var mb *entity.AdminMember
	if err = dao.AdminMember.Ctx(ctx).Where("username", in.Username).Scan(&mb); err != nil {
		err = gerror.Wrap(err, consts.ErrorORM)
		return
	}

	if mb == nil {
		err = gerror.New("账号不存在")
		return
	}

	res = new(adminin.LoginModel)
	res.Id = mb.Id
	res.Username = mb.Username
	if mb.Salt == "" {
		err = gerror.New("用户信息错误")
		return
	}

	if err = simple.CheckPassword(in.Password, mb.Salt, mb.PasswordHash); err != nil {
		return
	}

	if mb.Status != consts.StatusEnabled {
		err = gerror.New("账号已被禁用")
		return
	}

	res, err = s.handleLogin(ctx, mb, consts.AppAdmin)
	return
}

// MobileLogin 手机号登录
func (s *sAdminSite) MobileLogin(ctx context.Context, in *adminin.MobileLoginInp) (res *adminin.LoginModel, err error) {

	defer func() {
		service.SysLoginLog().Push(ctx, &sysin.LoginLogPushInp{Response: res, Err: err})
	}()

	if in.Code != consts.SmsSuperCode {
		err = service.SysSmsLog().VerifyCode(ctx, &sysin.VerifyCodeInp{
			Event:  consts.SmsTemplateLogin,
			Mobile: in.Mobile,
			Code:   in.Code,
		})
	}

	if err != nil {
		return
	}

	// 获取账号信息
	var mb *entity.AdminMember
	mod := dao.AdminMember.Ctx(ctx).Where("mobile", in.Mobile)
	// 登录平台
	switch in.Platform {
	// 门店
	case consts.PlatformStore:
		mod = mod.Where("role_id", consts.RoleStore) // 门店角色
	// 抖音
	case consts.PlatformDouyin:
		mod = mod.WhereIn("role_id", consts.RoleAllUser) // 用户角色
	// 用户
	default:
		mod = mod.WhereIn("role_id", consts.RoleAllUser) // 用户角色
	}
	if err = mod.Scan(&mb); err != nil {
		err = gerror.Wrap(err, consts.ErrorORM)
		return
	}

	if mb == nil {
		if in.Platform == "" || in.Platform == consts.PlatformStore {
			err = gerror.New("手机号未注册")
			return
		}
		mb = &entity.AdminMember{
			Avatar: "https://imgs.ynz666.com/test/2025/01/08/MzQ3MGEyMWE3NzYwNmM4N2Y2M2VjNGRkZWJhNGNhMzA=.png", // 默认头像
			DeptId: 114,                                                                                        // 休闲吧
			Salt:   grand.S(12),
			//Pid:    1,
			//Level:  2,
			//Tree:   "tr_1 tr_2 tr_213 tr_212 tr_211 ",
			Status: consts.StatusEnabled,
			Mobile: in.Mobile,
		}
		switch in.Platform {
		//case consts.PlatformStore:
		//	store, err2 := service.SysStore().ViewPhone(ctx, in.Mobile)
		//	if err2 != nil || store == nil {
		//		err = gerror.New("商家手机号未注册")
		//		return
		//	}
		//	mb.RoleId = consts.RoleStore
		//	mb.RealName = store.Name
		//	mb.StoreId = store.Id
		case consts.PlatformDouyin:
			mb.RoleId = consts.RoleUser                          // 用户
			mb.Source = consts.AdminMemberSourceDouYin           // 抖音
			mb.MemberStatus = consts.AdminMemberMemberStatusUser // 非会员
			mb.Label = gjson.New([]string{consts.AdminMemberLabelGuest})
		}
		id, err := dao.AdminMember.Ctx(ctx).Data(mb).OmitEmptyData().InsertAndGetId()
		if err != nil {
			err = gerror.New("登录失败，请稍后重试！")
			g.Log().Error(ctx, err)
			return nil, err
		}
		switch in.Platform {
		//case consts.PlatformStore:
		//	_, err = dao.StoreMember.Ctx(ctx).Data(entity.StoreMember{
		//		StoreId:  mb.StoreId,
		//		MemberId: id,
		//		IsAdmin:  1,
		//	}).Insert()
		//	if err != nil {
		//		err = gerror.New("登录失败，请稍后重试！")
		//		g.Log().Error(ctx, err)
		//		return nil, err
		//	}
		case consts.PlatformDouyin:
			err = service.AdminMember().UpdateInfo(ctx, &adminin.MemberUpdateInfoInp{
				ID:       id,
				Nickname: fmt.Sprintf("用户%d", id),
			})
			if err != nil {
				err = gerror.New("登录失败，请稍后重试！")
				g.Log().Error(ctx, err)
				return nil, err
			}
		}
		mb.Id = id
		mb.Nickname = fmt.Sprintf("用户%d", id)
	}

	res = new(adminin.LoginModel)
	res.Id = mb.Id
	// 设置登录日志用户名
	if in.Platform == consts.PlatformStore {
		mb.Username = mb.RealName
	} else if mb.Nickname == "" {
		mb.Username = fmt.Sprintf("用户%d", mb.Id)
	} else {
		// 使用微信昵称
		mb.Username = mb.Nickname
	}

	if mb.Status != consts.StatusEnabled {
		err = gerror.New("账号已被禁用")
		return
	}

	app := consts.AppAdmin
	if in.Platform == consts.PlatformStore || in.Platform == consts.PlatformDouyin {
		app = consts.AppApi
	}
	res, err = s.handleLogin(ctx, mb, app)
	return
}

// AppLogin 应用登录（微信小程序）
func (s *sAdminSite) AppLogin(ctx context.Context, in *apiin.AppLoginInp) (res *adminin.LoginModel, err error) {
	defer func() {
		service.SysLoginLog().Push(ctx, &sysin.LoginLogPushInp{Response: res, Err: err})
	}()

	sessionModel, err := service.CommonWechat().JsCode2Session(ctx, in.Code)
	if err != nil {
		g.Log().Error(ctx, err)
		return nil, gerror.New("登录失败，请稍后重试！")
	}

	var mb *entity.AdminMember
	if err = dao.AdminMember.Ctx(ctx).Where("open_id", sessionModel.OpenID).Scan(&mb); err != nil {
		err = gerror.Wrap(err, consts.ErrorORM)
		return
	}

	if mb == nil {
		mb = &entity.AdminMember{
			OpenId:       sessionModel.OpenID,
			Avatar:       "https://imgs.ynz666.com/test/2025/01/08/MzQ3MGEyMWE3NzYwNmM4N2Y2M2VjNGRkZWJhNGNhMzA=.png", // 默认头像
			DeptId:       114,                                                                                        // 休闲吧
			RoleId:       consts.RoleUser,                                                                            // 用户
			MemberStatus: consts.AdminMemberMemberStatusUser,                                                         // 非会员
			Label:        gjson.New([]string{consts.AdminMemberLabelGuest}),                                          // 标签
			Source:       consts.AdminMemberSourceNaturalRegister,                                                    // 自然注册
			Salt:         grand.S(12),
			InviteCode:   grand.S(12),
			Pid:          0,
			Level:        1,
			Tree:         "",
			Status:       consts.StatusEnabled,
		}
		if in.ProductId != 0 {
			product, _ := service.Product().GetByProductId(ctx, &apiin.GetByProductIdInp{
				ProductId: in.ProductId,
			})
			mb.Source = consts.AdminMemberSourceProductFission // 商品裂变
			if product.AgentId != 0 {
				mb.Pid = product.AgentId // 销氪ID
			}
		} else if in.InviteCode != "" {
			pid, _ := service.AdminMember().GetIdByCode(ctx, &adminin.GetIdByCodeInp{
				Code: in.InviteCode,
			})
			if pid != nil {
				mb.Pid = pid.Id                                // 邀请人ID
				mb.Source = consts.AdminMemberSourceUserInvite // 用户邀请
			}
		} else if in.UserId != 0 {
			mb.Pid = in.UserId                             // 邀请人ID
			mb.Source = consts.AdminMemberSourceUserInvite // 用户邀请
		}
		if mb.Pid != 0 {
			var pmb *entity.AdminMember
			if err = dao.AdminMember.Ctx(ctx).WherePri(mb.Pid).Scan(&pmb); err == nil {
				mb.Level = pmb.Level + 1
				mb.Tree = tree.GenLabel(pmb.Tree, mb.Pid)
			}
		}
		id, err2 := dao.AdminMember.Ctx(ctx).Data(mb).OmitEmptyData().InsertAndGetId()
		if err2 != nil {
			err = gerror.New("登录失败，请稍后重试！")
		}
		err = service.AdminMember().UpdateInfo(ctx, &adminin.MemberUpdateInfoInp{
			ID:       id,
			Nickname: fmt.Sprintf("用户%d", id),
		})
		if err != nil {
			err = gerror.New("登录失败，请稍后重试！")
		}
		mb.Id = id
		mb.Nickname = fmt.Sprintf("用户%d", id)
	} else if (in.InviteCode != "" || in.UserId != 0) && mb.Pid == 0 {
		count, err2 := dao.AdminMember.Ctx(ctx).WhereLike(dao.AdminMember.Columns().Tree, tree.GenLabel(mb.Tree, mb.Id)+"%").Count()
		if err2 != nil {
			err = gerror.Wrap(err2, "获取用户信息失败，请稍后重试！")
			return
		}
		if count == 0 {
			if in.InviteCode != "" {
				pid, _ := service.AdminMember().GetIdByCode(ctx, &adminin.GetIdByCodeInp{
					Code: in.InviteCode,
				})
				if pid != nil {
					mb.Pid = pid.Id // 邀请人ID
				}
			} else if in.UserId != 0 {
				mb.Pid = in.UserId // 邀请人ID
			}
			mb.Source = consts.AdminMemberSourceUserInvite // 用户邀请
			if mb.Pid != 0 {
				var pmb *entity.AdminMember
				if err = dao.AdminMember.Ctx(ctx).WherePri(mb.Pid).Scan(&pmb); err == nil {
					mb.Level = pmb.Level + 1
					mb.Tree = tree.GenLabel(pmb.Tree, mb.Pid)
				}
				_, err = service.AdminMember().Model(ctx).Data(g.Map{
					dao.AdminMember.Columns().Source: mb.Source,
					dao.AdminMember.Columns().Pid:    mb.Pid,
					dao.AdminMember.Columns().Tree:   mb.Tree,
					dao.AdminMember.Columns().Level:  mb.Level,
				}).Where(dao.AdminMember.Columns().Id, mb.Id).Update()
				if err != nil {
					glog.Errorf(ctx, "绑定失败: %s", err.Error())
				}
			}
		}
	}

	res = new(adminin.LoginModel)
	res.Id = mb.Id
	// 设置登录日志用户名
	if mb.Nickname == "" {
		mb.Username = fmt.Sprintf("用户%d", mb.Id)
	} else {
		// 使用微信昵称
		mb.Username = mb.Nickname
	}

	mb.SessionKey = sessionModel.SessionKey

	if mb.Status != consts.StatusEnabled {
		err = gerror.New("账号已被冻结")
		return
	}

	res, err = s.handleLogin(ctx, mb, consts.AppApi)
	if err != nil {
		return nil, err
	}
	key := service.CommonWechat().GetCacheKey(consts.WechatAuthorizeOpenId, token.GetAuthKey(res.Token))
	cache.Instance().Set(ctx, key, in.Code, time.Hour*24*7)
	return
}

// handleLogin .
func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember, app string) (res *adminin.LoginModel, err error) {
	role, dept, err := s.getLoginRoleAndDept(ctx, mb.RoleId, mb.DeptId)
	if err != nil {
		return nil, err
	}

	user := &model.Identity{
		Id:         mb.Id,
		Pid:        mb.Pid,
		DeptId:     dept.Id,
		DeptType:   dept.Type,
		RoleId:     role.Id,
		RoleKey:    role.Key,
		Username:   mb.Username,
		RealName:   mb.RealName,
		Avatar:     mb.Avatar,
		Email:      mb.Email,
		Mobile:     mb.Mobile,
		Label:      mb.Label,
		App:        app,
		LoginAt:    gtime.Now(),
		SessionKey: mb.SessionKey,
	}

	lt, expires, err := token.Login(ctx, user)
	if err != nil {
		return nil, err
	}

	res = &adminin.LoginModel{
		Username: user.Username,
		Id:       user.Id,
		Token:    lt,
		Expires:  expires,
	}
	return
}

// getLoginRoleAndDept 获取登录的角色和部门信息
func (s *sAdminSite) getLoginRoleAndDept(ctx context.Context, roleId, deptId int64) (role *entity.AdminRole, dept *entity.AdminDept, err error) {
	if err = dao.AdminRole.Ctx(ctx).Fields("id,key,status").WherePri(roleId).Scan(&role); err != nil {
		err = gerror.Wrap(err, consts.ErrorORM)
		return
	}

	if role == nil {
		err = gerror.New("角色不存在或已被删除")
		return
	}

	if role.Status != consts.StatusEnabled {
		err = gerror.New("角色已被禁用，如有疑问请联系管理员")
		return
	}

	if err = dao.AdminDept.Ctx(ctx).Fields("id,type,status").WherePri(deptId).Scan(&dept); err != nil {
		err = gerror.Wrap(err, "获取部门信息失败，请稍后重试！")
		return
	}

	if dept == nil {
		err = gerror.New("部门不存在或已被删除")
		return
	}

	if dept.Status != consts.StatusEnabled {
		err = gerror.New("部门已被禁用，如有疑问请联系管理员")
		return
	}
	return
}

// BindUserContext 绑定用户上下文
func (s *sAdminSite) BindUserContext(ctx context.Context, claims *model.Identity) (err error) {
	//// 如果不想每次访问都重新加载用户信息，可以放开注释。但在本次登录未失效前，用户信息不会刷新
	// contexts.SetUser(ctx, claims)
	// return

	var mb *entity.AdminMember
	if err = dao.AdminMember.Ctx(ctx).WherePri(claims.Id).Scan(&mb); err != nil {
		err = gerror.Wrap(err, "获取用户信息失败，请稍后重试！")
		return
	}

	if mb == nil {
		err = gerror.Wrap(err, "账号不存在或已被删除！")
		return
	}

	if mb.Status != consts.StatusEnabled {
		err = gerror.New("账号已被禁用，如有疑问请联系管理员")
		return
	}

	role, dept, err := s.getLoginRoleAndDept(ctx, mb.RoleId, mb.DeptId)
	if err != nil {
		return err
	}

	user := &model.Identity{
		Id:       mb.Id,
		Pid:      mb.Pid,
		DeptId:   dept.Id,
		DeptType: dept.Type,
		RoleId:   mb.RoleId,
		RoleKey:  role.Key,
		Username: mb.Username,
		RealName: mb.RealName,
		Avatar:   mb.Avatar,
		Email:    mb.Email,
		Mobile:   mb.Mobile,
		Label:    mb.Label,
		App:      claims.App,
		LoginAt:  claims.LoginAt,
	}

	contexts.SetUser(ctx, user)
	return
}
